home *** CD-ROM | disk | FTP | other *** search
/ Games of Daze / Infomagic - Games of Daze (Summer 1995) (Disc 1 of 2).iso / x2ftp / msdos / ai / fuzzy / token.b < prev    next >
Text File  |  1986-11-29  |  31KB  |  800 lines

  1.  
  2. -------------------------------------------------------------------------------
  3. --                                                                           --
  4. --  Library Unit:  Token  --  Get token package                              --
  5. --                                                                           --
  6. --  Author:  Bradley L. Richards                                             --
  7. --                                                                           --
  8. --     Version     Date     Notes . . .                                      --
  9. --    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -    --
  10. --       1.0     6 Feb 86   Initial Version                                  --
  11. --       1.1    25 Feb 86   All basic routines completed                     --
  12. --       1.2    13 Mar 86   Added reserved words.  Split Ada and Fuzzy       --
  13. --                            Prolog via conditional compilation             --
  14. --       1.3    22 May 86   Revised lots of Fuzzy Prolog stuff to make it    --
  15. --                            work; adding reserved words, etc..             --
  16. --       1.4    19 Jun 86   Use revised io package and data_def              --
  17. --       2.0    20 Jun 86   Token_type extracted into package Data_def       --
  18. --       2.05   13 Jul 86   Split into separate spec and body files          --
  19. --       2.1    21 Jul 86   Demonstration Version                            --
  20. --    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -    --
  21. --                                                                           --
  22. --  Library units used:       io -- read source file and produce listing     --
  23. --                       listing -- insert messages in listing               --
  24. --                      data_def -- common data definitions                  --
  25. --                                                                           --
  26. --  Description:  This package reads the source file (via "io") and parses   --
  27. --     out individual tokens.  There are three defined types which calling   --
  28. --     routines must use.  Token_type defines the legal kinds of tokens      --
  29. --     which may be returned.  Token_record is a variant record (with a      --
  30. --     discriminant of token_type) which is used to hold tokens.  Finally,   --
  31. --     token_ptr is the access type to the dynamically allocated             --
  32. --     token_records.                                                        --
  33. --          This package must be initialized by calling start_token.  Then   --
  34. --     subsequent calls to get_token will return one token each.  Note       --
  35. --     that end-of-file is indicated by the special token "end_of_file."     --
  36. --                                                                           --
  37. --          Note that this package is designed to be used with multiple      --
  38. --     languages.  In order to share the code and ensure that generic        --
  39. --     changes are made to all versions, this package is kept in a master    --
  40. --     source file which contains conditional compilation directives         --
  41. --     for use by the Ada preprocessor "pp."                                 --
  42. --                                                                           --
  43. -------------------------------------------------------------------------------
  44. --                                                                           --
  45. --                           Package Body                                    --
  46. --                                                                           --
  47. -------------------------------------------------------------------------------
  48.  
  49. package body token is
  50.  
  51.  
  52.   seen_end_of_file : boolean;
  53.  
  54.   procedure get_token is separate;
  55.  
  56.         --------------------------------------------
  57.         --  Initialization and Utility functions  --
  58.         --         in alphabetical order          --
  59.         --------------------------------------------
  60.  
  61.   --
  62.   --   start_token -- This routine makes the first call to get_char, so that
  63.   --                 get_token will have something to look at.
  64.   --
  65.   procedure start_token(source_file, listing_file : in string) is
  66.     begin
  67.       seen_end_of_file := false;
  68.       --
  69.       --  for Fuzzy Prolog use a lookahead of 1.  A lookahead of 2 is too
  70.       --  awkward to interactive i/o
  71.       --
  72.       start_io(source_file, listing_file, 1);
  73.       start_listing;
  74.       get_char;  -- get first character
  75.     end start_token;
  76.  
  77.  
  78.   --
  79.   --   stop_token -- This routine cleans up whatever needs to be done at the
  80.   --                 end of parsing a file
  81.   --
  82.   procedure stop_token is
  83.     begin
  84.       stop_io;
  85.     end stop_token;
  86.  
  87.  
  88.   --
  89.   --   skip_rest_of_token -- When a token_fetching routine encounters an error
  90.   --                         it generally returns the last valid value it had,
  91.   --                         and then wants to skip over the rest of the
  92.   --                         erroneous token.  This routine implements this by
  93.   --                         skipping characters until it encounters one which
  94.   --                         should not be embedded in a token.
  95.   --                            Note that it will stop on many characters which
  96.   --                         are not legal delimiters in the language.  This
  97.   --                         allows these characters to be flagged as seperate
  98.   --                         errors.
  99.   --
  100.   procedure skip_rest_of_token is
  101.     begin
  102.       while not valid_ending(look_ahead_char) loop
  103.     get_char;
  104.       end loop;
  105.     end skip_rest_of_token;
  106.  
  107.   --
  108.   --   valid_ending -- This routine looks for characters which signal the end
  109.   --                   of a token.  These characters may or may not technically
  110.   --                   be delimiters.
  111.   --
  112.   --     Characters accepted by this routine are:
  113.   --
  114.   --  space ! $ % & ( ) * + , - / : ; < = > ? @ [ \ ] ^ { | } ~ eot cr tab
  115.   --
  116.   --     Characters not accepted:  control characters (except eot, tab, & cr)
  117.   --                               digits, letters
  118.   --                               " # ' . _ ` ascii.rub
  119.   --
  120.   function valid_ending( char : character ) return boolean is
  121.     begin
  122.       if (char in ' '..'!') or
  123.          (char in '$'..'&') or
  124.      (char in '('..'-') or
  125.          (char = '/')       or
  126.          (char in ':'..'@') or
  127.          (char in '['..'^') or
  128.          (char in '{'..'~') or
  129.      (char = ascii.ht)  or
  130.          (char = ascii.eot) or
  131.      (char = ascii.cr)
  132.         then return true;
  133.         else return false;
  134.       end if;
  135.     end valid_ending;
  136.  
  137. end token;
  138.  
  139. --
  140. --   get_token -- This routine parses and returns the next token in the source
  141. --                file.  It expects look_ahead_char (the package variable) to be set
  142. --                to the next character to be processed.  The current character,
  143. --                therefore, has already been parsed.  All subroutines which
  144. --                get_token calls to handle the various token types must follow
  145. --                this convention.  The subroutine which finally identifies the
  146. --                token is allocates and defines the token_record which is to
  147. --                be returned.  The only time get_token itself defines the
  148. --                token_record is for the end_of_file.
  149. --
  150. separate(token)
  151. procedure get_token is
  152.  
  153.     token : token_ptr;
  154.     have_token : boolean := false;
  155.  
  156.     --
  157.     --     All token handling routines are separate.  This makes the code
  158.     --  easier to read than if several hundred lines were embedded here.
  159.     --
  160.     procedure get_character( token : out token_ptr) is separate;
  161.     procedure get_comment_or_minus( token : out token_ptr) is separate;
  162.     procedure get_greater_than( token : out token_ptr) is separate;
  163.     procedure get_identifier( token : out token_ptr) is separate;
  164.     procedure get_number( token : out token_ptr) is separate;
  165.     procedure get_string( token : out token_ptr) is separate;
  166.  
  167.     procedure get_fuzzy_backslash( token : out token_ptr) is separate;
  168.     procedure get_fuzzy_colon( token : out token_ptr) is separate;
  169.     procedure get_fuzzy_equal( token : out token_ptr) is separate;
  170.     procedure get_fuzzy_underline( token : out token_ptr) is separate;
  171.  
  172.   begin -- get_token
  173.     loop
  174.       case look_ahead_char is
  175.     --
  176.     --  skip embedded spaces
  177.     --
  178.     when ' '       => loop
  179.                 get_char;
  180.                 exit when look_ahead_char /= ' ';
  181.               end loop;
  182.     --
  183.     --  handle multi-character tokens
  184.     --
  185.     when 'A'..'Z'  => get_identifier(token); have_token := true;
  186.     when 'a'..'z'  => get_identifier(token); have_token := true;
  187.         when '0'..'9'  => get_number(token); have_token := true;
  188.     when '-' | '{' => get_comment_or_minus(token);
  189.               if token.is_a /= null_token then
  190.                 have_token := true;
  191.               end if;
  192.     when '"'       => get_string(token); have_token := true;
  193.     when '''       => get_character(token); have_token := true;
  194.     when '>'       => get_greater_than(token); have_token := true;
  195.     --
  196.     --  special cases:  tab, end-of-line, and end-of-file
  197.     --
  198.     when ascii.ht  => get_char;
  199.     when ascii.cr  => get_char;
  200.     when ascii.eot => token := new token_record'(is_a => end_of_file);
  201.               have_token := true;
  202.               if seen_end_of_file = true then
  203.                 raise unexpected_end_of_file;
  204.               else seen_end_of_file := true;
  205.               end if;
  206.  
  207.     when '='       => get_fuzzy_equal(token);
  208.               if token.is_a /= null_token then
  209.                 have_token := true;
  210.               end if;
  211.     when ':'       => get_fuzzy_colon(token);
  212.                           if token.is_a /= null_token then
  213.                             have_token := true;
  214.                           end if;
  215.  
  216.         --
  217.     --  now a sub-case statement to handle single character tokens
  218.     --
  219.     when others  =>
  220.       get_char;
  221.       have_token := true;                    -- almost certainly true
  222.       case current_char is
  223.         when '('  => token := new token_record'(is_a => left_paren);
  224.         when ')'  => token := new token_record'(is_a => right_paren);
  225.         when '*'  => token := new token_record'(is_a => asterisk);
  226.         when '+'  => token := new token_record'(is_a => plus);
  227.         when ','  => token := new token_record'(is_a => comma);
  228.         when ';'  => token := new token_record'(is_a => semicolon);
  229.         when '|'  => token := new token_record'(is_a => bar);
  230.  
  231.             when '!'  => token := new token_record'(is_a => cut);
  232.             when '/'  => token := new token_record'(is_a => slash);
  233.             when '<'  => token := new token_record'(is_a => less_than);
  234.             when '['  => token := new token_record'(is_a => left_bracket);
  235.             when ']'  => token := new token_record'(is_a => right_bracket);
  236.             when '^'  => token := new token_record'(is_a => hat);
  237.             when '.'  => token := new token_record'(is_a => period);
  238.             when '\'  => get_fuzzy_backslash(token);
  239.                            if token.is_a /= null_token then
  240.                              have_token := true;
  241.                            end if;
  242.                when '_'  => get_fuzzy_underline(token);
  243.                          if token.is_a /= null_token then
  244.                            have_token := true;
  245.                          end if;
  246.         when others => error(pointer,"illegal character");
  247.                skip_rest_of_token;
  248.                have_token := false;
  249.       end case;
  250.       end case;
  251.       exit when have_token;
  252.     end loop;
  253.     current_token := token;
  254.   end get_token;
  255.  
  256. -------------------------------------------------------------------------------
  257. --                                                                           --
  258. --                         Token Fetching Routines                           --
  259. --                                                                           --
  260. -------------------------------------------------------------------------------
  261.  
  262. --
  263. --   get_character -- This subroutine expects to see a single character enclosed
  264. --                    in single quotes.  Since this syntax is strictly defined,
  265. --                    there is no confusion when the character is a single
  266. --
  267. --                    Syntax:    character_spec ::=  ' ascii.character '
  268. --
  269. --                    Examples:  'a'  'M'  '#'  '''  'z'
  270. --
  271. separate(token.get_token)
  272. procedure get_character( token : out token_ptr) is
  273.   begin
  274.     get_char;
  275.     if (look_ahead_char = ascii.eot) or (look_ahead_char = ascii.cr) then
  276.       error(pointer,"invalid character literal");
  277.     else
  278.       get_char;
  279.       token := new token_record'(character_lit, current_char);
  280.       if look_ahead_char /= ''' then
  281.     error(pointer,"invalid character literal");
  282.     skip_rest_of_token;
  283.       else
  284.     get_char;
  285.       end if;
  286.     end if;
  287.   end get_character;
  288.  
  289. --
  290. --   get_comment_or_minus -- This routine handles two forms of comments.  The
  291. --                           first is initiated by two adjacent dashes and
  292. --                           terminated by the end-of-line.  If only a single
  293. --                           dash is found, a "minus" token is returned.  The
  294. --                           second form of comment is enclosed within scroll
  295. --                           brackets, and may cover multiple lines.  Nesting
  296. --                           level of the brackets is tracked, so comments may
  297. --                           be nested.  Comments return a "null_token."
  298. --
  299. --       Syntax:   minus  ::=  '-'
  300. --               comment  ::=  '--' comment 'ascii.cr'  |  '{' comment '}'
  301. --
  302. separate(token.get_token)
  303. procedure get_comment_or_minus( token : out token_ptr) is
  304.     nesting_level : natural := 1;
  305.   begin
  306.     get_char;
  307.     if current_char = '-' then
  308.       if look_ahead_char /= '-' then
  309.     token := new token_record'(is_a => minus);
  310.       else
  311.     loop
  312.       get_char;
  313.       exit when (current_char = ascii.cr) or (current_char = ascii.eot);
  314.     end loop;
  315.     token := new token_record'(is_a => null_token);
  316.       end if;
  317.     else -- current_char = '{'
  318.       loop
  319.     get_char;
  320.     if current_char = '}' then
  321.       nesting_level := nesting_level - 1;
  322.     elsif current_char = '{' then
  323.       nesting_level := nesting_level + 1;
  324.     elsif current_char = ascii.eot then
  325.       error(pointer, "unterminated comment block");
  326.       nesting_level := 0;
  327.     end if;
  328.     exit when (nesting_level = 0);
  329.       end loop;
  330.       token := new token_record'(is_a => null_token);
  331.     end if;
  332.   end get_comment_or_minus;
  333.  
  334. --
  335. --   get_greater_than -- The two tokens beginning with '>' are the
  336. --                       "greater_than" and the "greater_or_equal"
  337. --
  338. separate(token.get_token)
  339. procedure get_greater_than( token : out token_ptr) is
  340.   begin
  341.     get_char;
  342.     if look_ahead_char = '=' then
  343.       get_char;
  344.       token := new token_record'(is_a => greater_or_equal);
  345.     else
  346.       token := new token_record'(is_a => greater_than);
  347.     end if;
  348.   end get_greater_than;
  349.  
  350. --
  351. --   get_identifier -- Identifiers must begin with a letter (either upper or
  352. --                     lower case), and may then contain both letters and
  353. --                     digits.  Underlines may be embedded, but must separate
  354. --                     letters and digits.  Case is significant, only for
  355. --               Fuzzy Prolog, and only in that the first character, if
  356. --                     capitalized, indicates that the identifier is a variable.
  357. --                     Underlines are significant in all identifiers.  The only
  358. --                     limit on identifier length is line length, which is
  359. --                     controlled by package "special.io"
  360. --
  361. --       Syntax:   identifier  ::=  letter { [ '_' ] letter_or_digit }
  362. --
  363. separate(token.get_token)
  364. procedure get_identifier( token : out token_ptr) is
  365.     ptr : integer range 0..io.max_line_length := 0;
  366.     ident_name : string(1..io.max_line_length) := (others => ' ');
  367.     ident : name_ptr;
  368.     err_flg : boolean := false;
  369.     reserved : boolean := false;
  370.     convert : constant integer := character'pos('a') - character'pos('A');
  371.  
  372.     var_flg : boolean := false;
  373.  
  374.   --
  375.   --   This routine checks the identifier against the list of reserved
  376.   --   words.  If it is reserved, then "token" is set appropriately and
  377.   --   reserved is true.  The search method used is a simplistic hash table.
  378.   --
  379.   procedure check_reserved(length : in integer; ident : in string;
  380.                    reserved : out boolean; token : out token_ptr) is
  381.       type word_record is
  382.     record
  383.       word : string(1..9);
  384.       rw_token : token_type;
  385.     end record;
  386.       char_pos : constant array ('A'..'Z',1..2) of integer
  387.                         := ( (1,4),   (1,0),   (5,7),
  388.               (8,9),   (1,0),   (10,13), (14,15), (1,0),   (16,17),
  389.               (1,0),   (1,0),   (18,20), (21,21), (22,29), (30,31),
  390.               (32,33), (1,0),   (34,37), (38,42), (43,49), (50,50),
  391.               (51,51), (52,52), (1,0),   (1,0),   (1,0) );
  392.       words : constant array(1..52) of word_record :=
  393.        ( ("ASSERTA  ", rw_asserta),   ("ASSERTZ  ", rw_assertz),
  394.          ("ATOM     ", rw_atom),      ("ATOMIC   ", rw_atomic),
  395.          ("CALL     ", rw_call),      ("CLAUSE   ", rw_clause),
  396.          ("CONSULT  ", rw_consult),   ("DEBUGGING", rw_debugging),
  397.          ("DISPLAY  ", rw_display),   ("FAIL     ", rw_fail),
  398.          ("FLOAT    ", rw_float),     ("FUNCTOR  ", rw_functor),
  399.          ("FUZZY    ", rw_fuzzy),     ("GET      ", rw_get),
  400.          ("GET0     ", rw_get0),      ("INTEGER  ", rw_integer),
  401.          ("IS       ", rw_is),        ("LISTING  ", rw_listing),
  402.          ("LN       ", rw_ln),        ("LOG      ", rw_log),
  403.          ("MOD      ", rw_mod),       ("NAME     ", rw_name),
  404.          ("NL       ", rw_nl),        ("NODEBUG  ", rw_nodebug),
  405.          ("NONVAR   ", rw_nonvar),    ("NOSPY    ", rw_nospy),
  406.          ("NOT      ", rw_not),       ("NOTRACE  ", rw_notrace),
  407.          ("NUMBER   ", rw_number),    ("OP       ", rw_op),
  408.          ("ORG      ", rw_org),       ("PARSE    ", rw_parse),
  409.          ("PUT      ", rw_put),      ("READ     ", rw_read),
  410.          ("REPEAT   ", rw_repeat),    ("RESET    ", rw_reset),
  411.          ("RETRACT  ", rw_retract),
  412.          ("SEE      ", rw_see),       ("SEEING   ", rw_seeing),
  413.          ("SEEN     ", rw_seen),      ("SKIP     ", rw_skip),
  414.          ("SPY      ", rw_spy),       ("TAB      ", rw_tab),
  415.          ("TELL     ", rw_tell),      ("TELLING  ", rw_telling),
  416.          ("THRESHOLD", rw_threshold),
  417.          ("TOLD     ", rw_told),      ("TRACE    ", rw_trace),
  418.          ("TRUE     ", rw_true),      ("USER     ", rw_user),
  419.          ("VAR      ", rw_var),       ("WRITE    ", rw_write) );
  420.  
  421.       fail, found : boolean := false;
  422.       which : integer;
  423.     begin
  424.       for i in  char_pos(ident(1),1) .. char_pos(ident(1),2)  loop
  425.     for j in 2..length loop
  426.       if ident(j) < words(i).word(j) then
  427.         fail := true;
  428.       elsif ident(j) > words(i).word(j) then
  429.         exit;
  430.       else
  431.         if j = length then
  432.           if j = 9 then
  433.         found := true; which := i;
  434.           elsif words(i).word(j+1) = ' ' then
  435.         found := true; which := i;
  436.           else
  437.         exit;
  438.           end if;
  439.         end if;
  440.       end if;
  441.       exit when fail or found;
  442.     end loop;
  443.     exit when fail or found;
  444.       end loop;
  445.       if found then
  446.         token := new token_record'(reserved_word, words(which).rw_token);
  447.       end if;
  448.       reserved := found;
  449.     end check_reserved;
  450.  
  451.   begin
  452.  
  453.     if look_ahead_char in 'A'..'Z' then -- it's a Fuzzy Prolog variable
  454.       var_flg := true;
  455.     end if;
  456.  
  457.     loop
  458.       get_char;
  459.       ptr := ptr + 1;
  460.       if (current_char = '_') or else
  461.      (current_char in 'A'..'Z') or else
  462.      (current_char in 'a'..'z') or else
  463.      (current_char in '0'..'9') then
  464.           ident_name(ptr) := current_char;
  465.       else
  466.     error(pointer,"invalid character in identifier");
  467.     skip_rest_of_token;
  468.     err_flg := true;
  469.     exit;
  470.       end if;
  471.       if current_char = '_' and (not ((look_ahead_char in 'A'..'Z') or else
  472.                           (look_ahead_char in 'a'..'z') or else
  473.                           (look_ahead_char in '0'..'9'))) then
  474.     error(pointer,"underlines must separate letters or digits");
  475.     skip_rest_of_token;
  476.     err_flg := true;
  477.       end if;
  478.       if current_char in 'a'..'z' then
  479.     ident_name(ptr) := character'val(character'pos(current_char) - convert);
  480.       end if;
  481.       exit when valid_ending(look_ahead_char) or err_flg;
  482.       exit when look_ahead_char = '.';       -- required to detect end of clause
  483.     end loop;
  484.     
  485.     if ptr <= 9 then
  486.       check_reserved(ptr, ident_name(1..ptr), reserved, token);
  487.     end if;
  488.     
  489.     if reserved then
  490.       if var_flg then
  491.     error(pointer,"reserved words may not begin with capital letters");
  492.       end if;
  493.     elsif var_flg then
  494.       ident := new name_record'(ptr, ident_name(1..ptr));
  495.       token := new token_record'(variable, ident);
  496.     else
  497.       ident := new name_record'(ptr, ident_name(1..ptr));
  498.       token := new token_record'(identifier, ident);
  499.     end if;
  500.   end get_identifier;
  501.  
  502. --
  503. --   get_number -- This subroutine parses tokens which begin with a digit.  This
  504. --                 means integer and floating point numbers, either of which may
  505. --                 be based (legal bases are 2-16).
  506. --
  507. --      Syntax:       number  ::=  value  |  based_value
  508. --               based_value  ::=  base '#' value '#'
  509. --                      base  ::=  integer
  510. --                     value  ::=  integer  |  float
  511. --                   integer  ::=  digit { ['_'] digit }
  512. --                     float  ::=  integer '.' integer
  513. --
  514. separate(token.get_token)
  515. procedure get_number( token : out token_ptr ) is
  516.  
  517.     base : integer := 10;               -- default is base 10
  518.     digit : integer;
  519.     fp_decimal : float := 1.0;          -- factor for digits after decimal point
  520.     fp_num : float;
  521.     int_num : integer := 0;             -- initial value is 0
  522.     based, done, err_flg, fp : boolean := false;
  523.  
  524.     max_int_div_10 : constant integer := (integer'last/10);
  525.     max_int_last_digit : constant integer := (integer'last - 10*max_int_div_10);
  526.  
  527.     --
  528.     --   digit_val -- Converts a single character to a number in the current
  529.     --                base.  No error checking; the character must have been
  530.     --                checked by is_a_digit.
  531.     --
  532.     function digit_val(char : in character; base : in integer) return integer is
  533.     char_val : integer;
  534.       begin
  535.     char_val := character'pos(char) - character'pos('0');
  536.     if char_val > 9 then -- letter A-F or a-f
  537.       if char >= 'a' then -- lower case
  538.         char_val := char_val - 39;
  539.       else -- upper case
  540.         char_val := char_val - 7;
  541.       end if;
  542.     end if;
  543.     return char_val;
  544.       end digit_val;
  545.  
  546.     --
  547.     --  is_a_digit -- check a character to see if it is a valid digit in the
  548.     --                current base.
  549.     --
  550.     function is_a_digit(char : in character; base : in integer) return boolean is
  551.     char_pos : integer;
  552.       begin
  553.     char_pos := character'pos(char) - character'pos('0');
  554.         if char_pos < 0 then -- below digits
  555.       return false;
  556.     elsif char_pos < 10 then -- it's a digit
  557.       if char_pos < base then -- within the base
  558.         return true;
  559.       else
  560.         return false;
  561.       end if;
  562.     elsif char_pos < 17 then
  563.       return false;
  564.     elsif char_pos < (base + 7) then -- a digit A-F in the base
  565.       return true;
  566.     end if;
  567.     char_pos := char_pos - 32;                 -- check for lower case
  568.     if (char_pos < 17) or (char_pos >= (base+7)) then
  569.       return false;
  570.     else
  571.       return true;
  572.     end if;
  573.       end is_a_digit;
  574.  
  575.   begin -- get_number
  576.     loop
  577.     get_char;  -- get the next numeric char
  578.         if is_a_digit(current_char, base) then
  579.       digit := digit_val(current_char, base);
  580.       if fp then -- we're building a floating point number
  581.         fp_decimal := fp_decimal / float(base); -- adjust value of digit
  582.         fp_num := fp_num + float(digit) * fp_decimal;
  583.       else -- an integer (at least, so far)
  584.         if (int_num > max_int_div_10) or
  585.           ((int_num = max_int_div_10) and (digit > max_int_last_digit)) then
  586.           error(pointer,"integer too large");
  587.           err_flg := true;
  588.         else
  589.           int_num := int_num * base + digit;
  590.         end if;
  591.       end if;
  592.     elsif current_char = '_' then -- ignore underline when separating digits
  593.       if not is_a_digit(look_ahead_char, base) then
  594.         error(pointer,"underline must separate digits");
  595.         err_flg := true;
  596.       end if;
  597.     elsif current_char = '#' then -- deal with based number
  598.       if based then -- already working on a based number so this is the end
  599.         done := true;
  600.       else -- if legal, current value becomes the new base
  601.         if fp or (int_num < 2) or (int_num > 16) then -- illegal
  602.           error(pointer,"illegal base");
  603.           err_flg := true;
  604.          else
  605.           base := int_num;
  606.           int_num := 0;
  607.           based := true;
  608.           if not is_a_digit(look_ahead_char, base) then
  609.             error(pointer,"base declaration must be followed by" &
  610.                   " an appropriate based number");
  611.             err_flg := true;
  612.           end if;
  613.         end if;
  614.       end if;
  615.     elsif current_char = '.' then -- deal with floating point number
  616.       if fp then
  617.         error(pointer,"extra decimal point");
  618.         err_flg := true;
  619.       elsif not is_a_digit(look_ahead_char, base) then
  620.         error(pointer,"decimal point must be followed by digit");
  621.         err_flg := true;
  622.       else -- current value is to left of decimal point in fp_num
  623.         fp_num := float(int_num);
  624.         fp := true;
  625.       end if;
  626.     else -- we don't know what the heck we got . . .
  627.       if based then
  628.         error(pointer,"illegal character in based number");
  629.       else  error(pointer,"illegal character in number");
  630.     end if;
  631.     err_flg := true;
  632.       end if;
  633.       if valid_ending(look_ahead_char) then -- at end of number
  634.     done := true;
  635.       end if;
  636.  
  637.  
  638.       --
  639.       --  special case:  if a floating point number has been found and a
  640.       --                 period is coming, it may be the end of the clause.
  641.       --                 For example:  a(X) :- X is 7.0.
  642.       --                 There appears to be no reasonable way to allow this
  643.       --                 with integers, however.
  644.       --
  645.       if fp and (look_ahead_char = '.') then
  646.     done := true;
  647.       end if;
  648.  
  649.       exit when (err_flg or done);
  650.     end loop;
  651.     if err_flg then -- skip rest of number; return last valid value
  652.       skip_rest_of_token;
  653.     end if;
  654.     --
  655.     --  now define a new token record according to the type of number we've got
  656.     --
  657.     if fp then
  658.       token := new token_record'(float_num, fp_num);
  659.     else
  660.       token := new token_record'(integer_num, int_num);
  661.     end if;
  662.   end get_number;
  663.  
  664. --
  665. --   get_string -- Parses string literals delimited by double quotes ('"').
  666. --                 A double quote may be embedded in a string by placing two
  667. --                 of them side by side (example:  "abc""def" --> abc"def).
  668. --                 A null string (zero length) may be specified by not
  669. --                 enclosing any characters within the quotes (example:
  670. --                 "" --> null string).  Strings may not overlap end-of-line.
  671. --                 Line length is controlled by package "io"
  672. --
  673. --       syntax:  string  ::=  '"' text_of_string '"'
  674. --
  675. separate(token.get_token)
  676. procedure get_string( token : out token_ptr) is
  677.     ptr : integer range 0..io.max_line_length := 0;
  678.     string_value : string(1..io.max_line_length) := (others => ' ');
  679.     ident : name_ptr;
  680.   begin
  681.     get_char;
  682.     loop
  683.       if (look_ahead_char = ascii.cr) or (look_ahead_char = ascii.eot) then
  684.     error(pointer,"no terminating '""' for string");
  685.     get_char;
  686.     exit;
  687.       elsif look_ahead_char = '"' then
  688.     get_char;  -- throw away '"'
  689.     if look_ahead_char = '"' then -- embedded '"'
  690.       null;
  691.     else -- done with this string
  692.       exit;
  693.     end if;
  694.       end if;
  695.       get_char;
  696.       ptr := ptr + 1;
  697.       string_value(ptr) := current_char;
  698.     end loop;
  699.     if ptr = 0 then
  700.       error(pointer,"null string not allowed");
  701.       string_value(1) := '?';
  702.       ptr := 1;
  703.     end if;
  704.     ident := new name_record'(ptr, string_value(1..ptr));
  705.     token := new token_record'(identifier, ident);
  706.   end get_string;
  707.  
  708.                     -----------------------------
  709.                     --  Fuzzy Prolog routines  --
  710.                     -----------------------------
  711.  
  712. --
  713. --   get_fuzzy_backslash -- The backslash may be the start of either of the
  714. --                          non-equality operators in Fuzzy Prolog.  These
  715. --                          are '\=' and '\=='
  716. --
  717.    separate(token.get_token)
  718.    procedure get_fuzzy_backslash( token : out token_ptr) is
  719.      begin
  720.        if look_ahead_char /= '=' then
  721.          error(pointer,"must be '\=' or '\=='");
  722.          token := new token_record'(is_a => null_token);
  723.          skip_rest_of_token;
  724.        else
  725.          get_char;
  726.          if look_ahead_char = '=' then -- '\=='
  727.            token := new token_record'(is_a => not_equality);
  728.            get_char;
  729.          else -- '\='
  730.            token := new token_record'(is_a => not_equal);
  731.          end if;
  732.        end if;
  733.      end get_fuzzy_backslash;
  734.  
  735.  
  736. --
  737. --   get_fuzzy_colon -- The colon may only be the start of the implication
  738. --                      token ':-'
  739. --
  740.    separate(token.get_token)
  741.    procedure get_fuzzy_colon( token : out token_ptr) is
  742.      begin
  743.        get_char;
  744.        if look_ahead_char /= '-' then
  745.          error(pointer,"must be ':-'");
  746.          token := new token_record'(is_a => null_token);
  747.          skip_rest_of_token;
  748.        else
  749.          token := new token_record'(is_a => implication);
  750.          get_char;
  751.        end if;
  752.      end get_fuzzy_colon;
  753.  
  754. --
  755. --   get_fuzzy_equal -- The equal may be an equality test ('='), or may be
  756. --                      the start of equality ('=='), less than or equal to
  757. --                      ('=<'), or univ ('=..')
  758. --
  759.    separate(token.get_token)
  760.    procedure get_fuzzy_equal( token : out token_ptr) is
  761.      begin
  762.        get_char;
  763.        if look_ahead_char = '=' then -- equality
  764.          token := new token_record'(is_a => equality);
  765.          get_char;
  766.        elsif look_ahead_char = '<' then -- less than or equal to
  767.          token := new token_record'(is_a => less_or_equal);
  768.          get_char;
  769.        elsif look_ahead_char = '.' then -- may be univ?
  770.          get_char;
  771.          if look_ahead_char /= '.' then -- oops
  772.            error(pointer,"univ must be '=..'");
  773.            skip_rest_of_token;
  774.            token := new token_record'(is_a => null_token);
  775.          else -- univ
  776.            token := new token_record'(is_a => univ);
  777.            get_char;
  778.          end if;
  779.        else -- just plain old '=' (equal)
  780.          token := new token_record(equal);
  781.        end if;
  782.      end get_fuzzy_equal;
  783.  
  784.  
  785. --
  786. --   get_fuzzy_underline -- The underline in Fuzzy Prolog represents an
  787. --                          anonymous variable.  This routine merely ensures
  788. --                          that the underline is followed by a valid delimiter
  789. --
  790.    separate(token.get_token)
  791.    procedure get_fuzzy_underline( token : out token_ptr) is
  792.      begin
  793.        if valid_ending(look_ahead_char) then
  794.      token := new token_record(is_a => underline);
  795.        else
  796.      error(pointer,"identifiers may not begin with an underline");
  797.      token := new token_record(is_a => null_token);
  798.        end if;
  799.      end get_fuzzy_underline;
  800.